/**
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 *
 *
 * @format
 */

"use strict";
var _extends =
  Object.assign ||
  function(target) {
    for (var i = 1; i < arguments.length; i++) {
      var source = arguments[i];
      for (var key in source) {
        if (Object.prototype.hasOwnProperty.call(source, key)) {
          target[key] = source[key];
        }
      }
    }
    return target;
  };
let resolveConfig = (() => {
  var _ref = _asyncToGenerator(function*(path, cwd) {
    if (path) {
      return explorer.load(path);
    }

    const result = yield explorer.search(cwd);
    if (result == null) {
      // No config file found, return a default
      return {
        isEmpty: true,
        filepath: join(cwd || process.cwd(), "metro.config.stub.js"),
        config: {}
      };
    }

    return result;
  });
  return function resolveConfig(_x, _x2) {
    return _ref.apply(this, arguments);
  };
})();
let loadMetroConfigFromDisk = (() => {
  var _ref2 = _asyncToGenerator(function*(path, cwd, defaultConfigOverrides) {
    const resolvedConfigResults = yield resolveConfig(path, cwd);
    const configModule = resolvedConfigResults.config,
      filepath = resolvedConfigResults.filepath;
    const rootPath = dirname(filepath);

    const defaultConfig = yield getDefaultConfig(rootPath);

    if (typeof configModule === "function") {
      // Get a default configuration based on what we know, which we in turn can pass
      // to the function.

      const resultedConfig = yield configModule(defaultConfig);
      return resultedConfig;
    }

    return mergeConfig(defaultConfig, defaultConfigOverrides, configModule);
  });
  return function loadMetroConfigFromDisk(_x3, _x4, _x5) {
    return _ref2.apply(this, arguments);
  };
})();

/**
 * Load the metro configuration from disk
 * @param  {object} argv                    Arguments coming from the CLI, can be empty
 * @param  {object} defaultConfigOverrides  A configuration that can override the default config
 * @return {object}                         Configuration returned
 */ let loadConfig = (() => {
  var _ref3 = _asyncToGenerator(function*() {
    let argv =
      arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
    let defaultConfigOverrides =
      arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
    argv.config = overrideArgument(argv.config);

    const configuration = yield loadMetroConfigFromDisk(
      argv.config,
      argv.cwd,
      defaultConfigOverrides
    );

    // Override the configuration with cli parameters
    const configWithArgs = overrideConfigWithArguments(configuration, argv);

    const overriddenConfig = {};

    // The resolver breaks if "json" is missing from `resolver.sourceExts`
    const sourceExts = configWithArgs.resolver.sourceExts;
    if (!configWithArgs.resolver.sourceExts.includes("json")) {
      overriddenConfig.resolver = {
        sourceExts: [].concat(_toConsumableArray(sourceExts), ["json"])
      };
    }

    overriddenConfig.watchFolders = [configWithArgs.projectRoot].concat(
      _toConsumableArray(configWithArgs.watchFolders)
    );

    // Set the watchfolders to include the projectRoot, as Metro assumes that is
    // the case
    return mergeConfig(configWithArgs, overriddenConfig);
  });
  return function loadConfig() {
    return _ref3.apply(this, arguments);
  };
})();
function _toConsumableArray(arr) {
  if (Array.isArray(arr)) {
    for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++)
      arr2[i] = arr[i];
    return arr2;
  } else {
    return Array.from(arr);
  }
}
function _asyncToGenerator(fn) {
  return function() {
    var gen = fn.apply(this, arguments);
    return new Promise(function(resolve, reject) {
      function step(key, arg) {
        try {
          var info = gen[key](arg);
          var value = info.value;
        } catch (error) {
          reject(error);
          return;
        }
        if (info.done) {
          resolve(value);
        } else {
          return Promise.resolve(value).then(
            function(value) {
              step("next", value);
            },
            function(err) {
              step("throw", err);
            }
          );
        }
      }
      return step("next");
    });
  };
}
const cosmiconfig = require("cosmiconfig");
const getDefaultConfig = require("./defaults");
var _require = require("path");
const dirname = _require.dirname,
  resolve = _require.resolve,
  join = _require.join;
/**
 * Takes the last argument if multiple of the same argument are given
 */ function overrideArgument(arg) {
  if (arg == null) {
    return arg;
  }
  if (Array.isArray(arg)) {
    return arg[arg.length - 1];
  }
  return arg;
}
const explorer = cosmiconfig("metro", {
  searchPlaces: [
    "metro.config.js",
    "metro.config.json",
    "package.json",
    "rn-cli.config.js"
  ],
  loaders: {
    ".json": cosmiconfig.loadJson,
    ".yaml": cosmiconfig.loadYaml,
    ".yml": cosmiconfig.loadYaml,
    ".js": cosmiconfig.loadJs,
    ".es6": cosmiconfig.loadJs,
    noExt: cosmiconfig.loadYaml
  }
});
function mergeConfig(defaultConfig) {
  for (
    var _len = arguments.length,
      configs = Array(_len > 1 ? _len - 1 : 0),
      _key = 1;
    _key < _len;
    _key++
  ) {
    configs[_key - 1] = arguments[_key];
  } // If the file is a plain object we merge the file with the default config,
  // for the function we don't do this since that's the responsibility of the user
  return configs.reduce(
    (totalConfig, nextConfig) =>
      _extends({}, totalConfig, nextConfig, {
        resolver: _extends({}, totalConfig.resolver, nextConfig.resolver || {}),
        serializer: _extends(
          {},
          totalConfig.serializer,
          nextConfig.serializer || {}
        ),
        transformer: _extends(
          {},
          totalConfig.transformer,
          nextConfig.transformer || {}
        ),
        server: _extends({}, totalConfig.server, nextConfig.server || {})
      }),
    defaultConfig
  );
}
function overrideConfigWithArguments(config, argv) {
  // We override some config arguments here with the argv
  const output = { resolver: {}, serializer: {}, server: {}, transformer: {} };
  if (argv.port != null) {
    output.server.port = Number(argv.port);
  }
  if (argv.projectRoot != null) {
    output.projectRoot = argv.projectRoot;
  }
  if (argv.watchFolders != null) {
    output.watchFolders = argv.watchFolders;
  }
  if (argv.assetExts != null) {
    output.resolver.assetExts = argv.assetExts;
  }
  if (argv.sourceExts != null) {
    output.resolver.sourceExts = argv.sourceExts;
  }
  if (argv.platforms != null) {
    output.resolver.platforms = argv.platforms;
  }
  if (argv.providesModuleNodeModules != null) {
    output.resolver.providesModuleNodeModules = argv.providesModuleNodeModules;
  }
  if (argv["max-workers"] != null || argv.maxWorkers != null) {
    output.maxWorkers = Number(argv["max-workers"] || argv.maxWorkers);
  }
  if (argv.transformer != null) {
    output.transformer.babelTransformerPath = resolve(argv.transformer);
  }
  if (argv["reset-cache"] != null) {
    output.resetCache = argv["reset-cache"];
  }
  if (argv.resetCache != null) {
    output.resetCache = argv.resetCache;
  }
  if (argv.verbose === false) {
    output.reporter = { update: () => {} }; // TODO: Ask if this is the way to go
  }
  return mergeConfig(config, output);
}
module.exports = { loadConfig, resolveConfig, mergeConfig };
